Skip to content

Conversation

@TerjeBr
Copy link
Contributor

@TerjeBr TerjeBr commented Apr 3, 2018

This is an improvment to the MapQuest provider, that we talked about in #839

I have tried to stay away from the code in the Common directory, but I could not avoid some small minor changes there to make it all fit together. Hope this will be ok.

@willdurand willdurand requested a review from Nyholm April 3, 2018 15:52
@TerjeBr TerjeBr changed the title Map quest improvement MapQuest improvement Apr 3, 2018
@Nyholm
Copy link
Member

Nyholm commented Apr 3, 2018

Thank you for this PR.
I did a quick review and I see some issues when using a chain provider.
Do you really need a custom implementation of a query?

@TerjeBr
Copy link
Contributor Author

TerjeBr commented Apr 3, 2018

The custom implementation of a query is only to make it easier to use. It will work fine with a reqular query as well, as I wrote in the Readme.md

@Nyholm
Copy link
Member

Nyholm commented Apr 3, 2018

I would be way happier without the custom implementation. That would also avoid the changes in Common

@TerjeBr
Copy link
Contributor Author

TerjeBr commented Apr 3, 2018

I am sorry that you do not like the custom implementation of GeocodeQuery. Is there something in particular that bothers you about it? Something I can improve?

Or are you just opposed to having two GeocodeQuery implementations in general?

The additional one should work fine as a replacement for all the cases where the original one may be used.

Without the new GeocodeQuery implementation, you first have to create a GeocodeQuery with a dummy address string (it cannot be empty), and then clone it in order to have the address data on it. I found this to be a bit cumbersome, so that is why I added the new implementation.

@TerjeBr
Copy link
Contributor Author

TerjeBr commented Apr 3, 2018

It is a bit unfortunate that the interface Geocoder\Provider\Provider (that all the providers must implement) depends on the concrete classes Geocoder\Query\GeocodeQuery and Geocoder\Query\ReverseQuery instead of interfaces. When in addition those concrete classes are final it leaves no room for changing or improving the Query classes that are used in a graceful way.

I guess changing it to use interfaces is the "right" thing to do, but that will be a huge BC break. Removing the final keyword on the concrete classes is less invasive. If you choose to do that, you also might as well make the properties protected instead of private.

The only other alternative I can see is to declare that the Query classes are set in stone, and we can have no suggested changes or development on the Query classes. I think that would be a bit unfortunate thing to do.

@TerjeBr
Copy link
Contributor Author

TerjeBr commented Apr 13, 2018

Hi @Nyholm what/when will be the next step?

Copy link
Member

@Nyholm Nyholm left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Thank you for the reminder. I gave this another quick review.

About the massive changes in MapQuest: You are the expert here. I do not know much about that provider and the API.

About the changes in Common: I would be really happy if they are kept to a minimum.

/**
* @param string $text
*/
private function __construct(string $text)
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Why do you need to override a private constructor? Why not override the create function?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

It seemed to me to be logical that when the class is not final any more, child classes should be free to do a different __construct. But I can do as you say, and override the create function instead, no problem.

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I would like to keep the changes to the GeocodeQuery to a strict minimum.

return $this->executeQuery($url);
private function extractAddressFromQuery(CommonGeocodeQuery $query)
{
if ($query instanceof GetAddressInterface) {
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Sorry for my lack of understanding. Why do we need this check?

Dont we get the address either way? The way you implemented getAddress is:

public function getAddress()
{
      return $this->getData(static::DATA_KEY_ADDRESS);
}

Copy link
Contributor Author

@TerjeBr TerjeBr Apr 14, 2018

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This was my attempt at future proofing the code a bit.
This way for any GeocodeQuery object that implements the GetAddressInterface will just call the getAddess method, and we do not care where it is stored. If I could have changed the common GeocodeQuery class I would have left it at that.

The line

return $query->getData(GeocodeQuery::DATA_KEY_ADDRESS);

is just a fallback for if you are using a regular GeocodeQuery that does not implement the GetAddressInterface.

I think it is better to depend on interfaces instead of depending on a known constant in a specific class.

I implemented getAddress the way I did to have max interoparability with the common GeocodeQuery class, so that a programmer did not have to use the mapquest version of the GeocodeQuery class.

/**
* @var array
*/
private $data = [];
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Why do you need to modify the scope of $data? You could just introduce a new property on Geocoder\Provider\MapQuest\GeocodeQuery, right?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

My goal was that the new GeocodeQuery class could be a drop in and exchangeable with the old one. If I introduce a new property, I would also have to reimplement the withData method right?

It seemed easier for me to do it this way. If you agree to remove the final keyword, why cannot all properties be protected?

*
* @var string
*/
private $text;
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Is this needed? If you overwrite withText then there is no need for changing this to protected, right?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yes, I can overwrite all the methods to avoid changing the properties in the base class to from private to protected. But I thought the proper way was to change the visibilty of the properties, not to duplicate the code in the methods?

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

If you change the visibility like this we risk that someone writes code to make this mutable. I would like you not to change this.

/**
* @author Tobias Nyholm <tobias.nyholm@gmail.com>
*/
final class GeocodeQuery implements Query
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This hurts a little but Im fine with it =)

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Thank you for that. I am glad that you will at least allow this.

return $this->parseHttpResponse($response, $url);
}

protected function parseHttpResponse(ResponseInterface $response, string $url): string
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Thank you for this function.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

You are welcome.

return $this->text;
}

protected function formatAddress(Location $address): string
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This function seams to violate the single responsibility principle.

Copy link
Contributor Author

@TerjeBr TerjeBr Apr 14, 2018

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

How is that?

It is a protected method required by the above getText() method.
Should not a query containing an address have a method to convert that address to a one line text query, so that the query can be used also in other providers?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This is only meant as a default value for the text of the query, and will not be called if the user calls GoecodeQuery::withText him/her self to set the text portion of the query.

(Btw. "seams" = sömmar, "seems"= verkar)

return new self($address);
}

public function getAddress()
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This function is only use internally. It could be removed.

Copy link
Contributor Author

@TerjeBr TerjeBr Apr 14, 2018

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This is the implementation of the GetAddressInterface. It makes is possible to get the address out of the Query without knowing anything about how it is stored in the Query.

What do you mean it is only used internally? Other (user land) code code also have use of the ease of calling getAddress on the Query, if they want to get the addres from the query.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Also, since this GecodeQuery has a createFromAddress method, is it not natural that it then also has getAddress method?

This reminds me that may be I should have an withAddress method as well, in case the user wants to add the address or "change" it later. (Yes, I know it is an immutable object that does not change :-)

What do you think, should I add the method withAddress?


public static function createFromAddress(Location $address): self
{
return new self($address);
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Just do:

$foo = new self('');
return $foo->withData(static::DATA_KEY_ADDRESS], $address);

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

It is a bit more ineffecient, as it will create two objects instead of one, and then leave the first one to be garbage collected.
But yes, that is a valid way to do it if the methods in the base class cannot be changed to be protected instead of private.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Another thing is that $foo = new self('') will not work, it would trigger the exception InvalidArgument('Geocode query cannot be empty').

And if I have to put something in the $text property, what should it be? Since you want me to remove the formatAddress method I would be stuck with a GeocodeQuery that would return something weird if it is used in another provider that calls getText.

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

It is really not that inefficient. We are talking optimization on less than duoble quotes vs single quotes.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Ok, forget my inefficency arguments then. Please respond to the other points I raised.

@TerjeBr
Copy link
Contributor Author

TerjeBr commented Apr 14, 2018

Thank you for your review. I will wait for your response before I push a new set of changes.

@TerjeBr
Copy link
Contributor Author

TerjeBr commented Apr 26, 2018

@Nyholm any updates on this? I am still waiting for your comments.

@TerjeBr
Copy link
Contributor Author

TerjeBr commented May 14, 2018

@Nyholm Have you been on vacation or something?

Please continue the conversation so we can land this PR.

@TerjeBr
Copy link
Contributor Author

TerjeBr commented May 14, 2018

@Nyholm if we are going to change the Common\Query\GeocodeQuery class from final to non-final, do you not agree that it makes sense to also change the constructor and all the properties from private to protected?

Do you agree with the above statement? Or do you have any reason I am not aware of for wanting to keep it all as private?

Copy link
Member

@Nyholm Nyholm left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yes, I've been to vacation. =)

I really like the changes you do in the provider. The review process of this PR is is harder because you want to push many changes to the GeocodeQuery class. I find it hard to see scenarios where you really have to update that class. Most of your arguments are "it is easier"...

Try you restrict yourself to not update the common objects. You may send another PR with these changes later.

*
* @var string
*/
private $text;
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

If you change the visibility like this we risk that someone writes code to make this mutable. I would like you not to change this.

/**
* @param string $text
*/
private function __construct(string $text)
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I would like to keep the changes to the GeocodeQuery to a strict minimum.

TerjeBr pushed a commit to TerjeBr/Geocoder that referenced this pull request May 14, 2018
TerjeBr pushed a commit to TerjeBr/Geocoder that referenced this pull request May 14, 2018
@TerjeBr
Copy link
Contributor Author

TerjeBr commented May 14, 2018

Ok, If you will please accept #849 that I have made with the minimal changes to the common files, I will then after that make the necessary changes to this PR to reflect that.

Hopefully we can then move this PR forward.

@TerjeBr
Copy link
Contributor Author

TerjeBr commented May 14, 2018

@Nyholm for me to make the tests pass you need to tag a new version of https://github.com/geocoder-php/provider-integration-tests

@TerjeBr TerjeBr force-pushed the map-quest-improvement branch from 400434b to 4112648 Compare May 14, 2018 13:18
TerjeBr pushed a commit to TerjeBr/Geocoder that referenced this pull request May 14, 2018
@TerjeBr TerjeBr force-pushed the map-quest-improvement branch from 4112648 to 97a0ff6 Compare May 14, 2018 13:20
@TerjeBr
Copy link
Contributor Author

TerjeBr commented May 15, 2018

@Nyholm plase make a new version tag on the geocoder-php/provider-integration-tests repository so that I can update the composer.lock files so this will do the tests properly.

You will also need to merge #849 and add a tag to that before this PR can show green on all tests.

@TerjeBr
Copy link
Contributor Author

TerjeBr commented May 16, 2018

@Nyholm, have you gone on vacation again?

@TerjeBr
Copy link
Contributor Author

TerjeBr commented May 17, 2018

@Nyholm Are you celebrating Norways national day today?
Is that why you are not doing anything with this PR today?

@TerjeBr
Copy link
Contributor Author

TerjeBr commented May 21, 2018

@Nyholm ping

@TerjeBr
Copy link
Contributor Author

TerjeBr commented May 22, 2018

I have complied with everything you have requested, as far as I know.

If you want to merge just this pull request, that is fine, then you can ignore #849
Or if you want do do it in stages, you can merge #849 first, and then this one.

Please tell me if there is anything else you need for this to be merged.

@Nyholm
Copy link
Member

Nyholm commented May 22, 2018

Please tell me if there is anything else you need for this to be merged.

I'll try to be extra clear with what I mean.

This is the process I want to follow:

  1. You update this PR so you do not modify any classes in common
  2. I review it and after I'm happy I'll merge it
  3. (optional) You make a PR with changes in common that you can prove would significantly improve maintainability and user experience for at least a handful of providers.

@TerjeBr
Copy link
Contributor Author

TerjeBr commented May 22, 2018

Does that mean you are backtracking on what we already have discussed?

On 13th of April you agreed that I could at least change the Common\Query\GeocodeQuery by removing the final keyword. All the other changes I can code around, but I cannot code around that.

Less important are the changes to Http\Provider\AbstractHttpProvider (you even thanked me for adding a function there) and a small bugfix in Common\Model\AddressBuilder.
That is all the changes that you will find in #849

So, have you changed your stance so to not allow any changes at all in the common classes now?

@TerjeBr
Copy link
Contributor Author

TerjeBr commented May 24, 2018

And again we have this great wall of silence.

Is it too much to expect that you are able to respond to me within a day or so?
Does it have to be weeks between when we exchange comments on this PR? I find that very frustrating.

@Nyholm
Copy link
Member

Nyholm commented May 24, 2018

And again we have this great wall of silence.

Im doing open source work on my free time because I enjoy it. Doing open source is however not the only thing Im doing before and after normal work.
Im not sure if I intrepid your messages correctly, but I sense a hostile tone. (Correct me if Im wrong.) That makes it no fun to spend time helping you get your PR merged.

So, have you changed your stance so to not allow any changes at all in the common classes now?

I've not changed my mind. But I feel that you do not really listen when I say "restrict yourself from making changes in common." I know it is possible to add the features you want to add without any changes at all in common (as explained). I want you to see that as well. Since I want to help you to get this merged, I now ask you to not make any changes at all in common since that will ease reviewing and the decisions to merge is not too critical for the rest of the project.

You may think Im unfair or unreasonable. I do not want to discuss this anymore, but Im happy to review an updated PR.
If other maintainers think do not agree with me you are more welcome to say so.

@TerjeBr
Copy link
Contributor Author

TerjeBr commented May 24, 2018

I know it is possible to add the features you want to add without any changes at all in common (as explained).

This is not true.

It is impossible for me to continue without at least removing the final keyword from the Common\Query\GeocodeQuery class. I thought we already discussed that, and that you agreed to that change.

All the other ones I can code around, but not that one.

@TerjeBr
Copy link
Contributor Author

TerjeBr commented May 24, 2018

Shall I go ahead and make the PR so that the removing of the final keyword is the only change among the common files?

@TerjeBr
Copy link
Contributor Author

TerjeBr commented May 24, 2018

You are aware that I updated this PR on 14th of May right? And that that update made it only contain 3 small changes to the common files that I thought we were in agreement on?

If you were not aware of that, that may be the cause that we are talking past each other?

@TerjeBr
Copy link
Contributor Author

TerjeBr commented May 25, 2018

@Nyholm please answer me so I can go ahead with this PR

@TerjeBr
Copy link
Contributor Author

TerjeBr commented Jun 10, 2018

Since you do not give any answers, I will just have to assume. I assume then that I reached the correct conclusion when I said "make the PR so that the removing of the final keyword is the only change among the common files". I will go ahead and do just that.

@TerjeBr TerjeBr force-pushed the map-quest-improvement branch 2 times, most recently from fcd816b to b9d4e35 Compare June 10, 2018 13:05
@TerjeBr TerjeBr force-pushed the map-quest-improvement branch from d95a437 to c05351a Compare June 10, 2018 13:10
@TerjeBr
Copy link
Contributor Author

TerjeBr commented Jun 10, 2018

Ok now everything should be as you want it.

Please tag a new version for https://github.com/geocoder-php/provider-integration-tests
if you want all the tests to pass before you merge.

@unkind
Copy link
Contributor

unkind commented Jun 18, 2018

Without the new GeocodeQuery implementation, you first have to create a GeocodeQuery with a dummy address string (it cannot be empty), and then clone it in order to have the address data on it. I found this to be a bit cumbersome, so that is why I added the new implementation.

Do I understand right that you want to remove final just because you're missing named constructor like GeocodeQuery::location(...)?

Maybe it's better to improve GeocodeQuery model instead of inheriting it?

@unkind
Copy link
Contributor

unkind commented Jun 18, 2018

@Nyholm argh, removing final is a very bad sign in most cases. You can call me next time :)

@unkind
Copy link
Contributor

unkind commented Jun 18, 2018

After reading original topic, I think you can just make (static) function to create GeocodeQuery for MapQuest: MapQuest::createQuery(...).

By the way, why does Geocoder have Query interface? I thought you decided you throw it off: 40bdb8d

@Nyholm
Copy link
Member

Nyholm commented Jun 26, 2018

I've added a PR to your PR: TerjeBr#1

@TerjeBr
Copy link
Contributor Author

TerjeBr commented Jul 8, 2018

Ok, I am back from my vacation. Let's see what has happened here.

@TerjeBr
Copy link
Contributor Author

TerjeBr commented Jul 8, 2018

@unkind wrote:

Do I understand right that you want to remove final just because you're missing named constructor like GeocodeQuery::location(...)?

Maybe it's better to improve GeocodeQuery model instead of inheriting it?

You are right that it would be better to improve GeocodeQuery instead of inheriting it. That is what I asked about first in #839, if there could be a way to incorporate the address in the query. But that seems to be a no go from @Nyholm.

So, this quest of mine to inherit from GeocodeQuery is my attempt to circumvent that so mapquest users could have at least some level of user friendliness in the use of this liberary. But now that hope seems to be thwarted too, since he has obviously changed his mind and wont even allow the final keyword to be removed from the class (that he previously agreed to).

TerjeBr and others added 2 commits July 8, 2018 22:07
@TerjeBr
Copy link
Contributor Author

TerjeBr commented Jul 8, 2018

Ok, I hope this can finally be merged now then.

It fails the style guide check, but that was in the changes that @Nyholm introduced, so I guess that is ok.

@TerjeBr
Copy link
Contributor Author

TerjeBr commented Jul 20, 2018

Ping

Copy link
Member

@Nyholm Nyholm left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Great. Thank you

@Nyholm Nyholm merged commit 1fdce51 into geocoder-php:master Jul 20, 2018
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

4 participants